package repository

import (
	"context"
	"errors"
	"time"

	"gorm.io/gorm"

	"BookStack/internal/domain/navigation"
	appErrors "BookStack/pkg/errors"
	"BookStack/pkg/utils"
)

// NavigationRepository 导航仓储实现
type NavigationRepository struct {
	db *gorm.DB
}

// NewNavigationRepository 创建导航仓储
func NewNavigationRepository(db *gorm.DB) navigation.Repository {
	return &NavigationRepository{
		db: db,
	}
}

// GetByID 通过ID获取导航
func (r *NavigationRepository) GetByID(ctx context.Context, id uint) (*navigation.Navigation, error) {
	var nav navigation.Navigation
	if err := r.db.WithContext(ctx).First(&nav, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, appErrors.New(appErrors.ErrCodeNavigationNotFound, "")
		}
		return nil, err
	}
	return &nav, nil
}

// GetByPosition 通过位置获取导航
func (r *NavigationRepository) GetByPosition(ctx context.Context, position string) ([]*navigation.Navigation, error) {
	var navigations []*navigation.Navigation
	err := r.db.WithContext(ctx).
		Where("position = ? AND status = ?", position, navigation.StatusEnabled).
		Order("parent_id ASC, `order` ASC").
		Find(&navigations).Error
	if err != nil {
		return nil, err
	}
	return navigations, nil
}

// List 获取导航列表
func (r *NavigationRepository) List(ctx context.Context, page, pageSize int) ([]*navigation.Navigation, int64, error) {
	var navigations []*navigation.Navigation
	var total int64

	// 计算偏移量
	offset := (page - 1) * pageSize

	// 查询总数
	if err := r.db.Model(&navigation.Navigation{}).Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 查询数据
	if err := r.db.Offset(offset).Limit(pageSize).Find(&navigations).Error; err != nil {
		return nil, 0, err
	}

	return navigations, total, nil
}

// Create 创建导航
func (r *NavigationRepository) Create(ctx context.Context, navigation *navigation.Navigation) error {
	now := time.Now()
	if navigation.CreatedAt.IsZero() {
		navigation.CreatedAt = now
	}
	if navigation.UpdatedAt.IsZero() {
		navigation.UpdatedAt = now
	}
	return r.db.WithContext(ctx).Create(navigation).Error
}

// Update 更新导航
func (r *NavigationRepository) Update(ctx context.Context, navigation *navigation.Navigation) error {
	if navigation.UpdatedAt.IsZero() {
		navigation.UpdatedAt = time.Now()
	}
	return r.db.WithContext(ctx).Save(navigation).Error
}

// Delete 删除导航
func (r *NavigationRepository) Delete(ctx context.Context, id uint) error {
	return r.db.WithContext(ctx).Delete(&navigation.Navigation{}, id).Error
}

// CheckNameExists 检查名称是否存在
func (r *NavigationRepository) CheckNameExists(ctx context.Context, name, position string, excludeID uint) (bool, error) {
	var count int64
	query := r.db.WithContext(ctx).Model(&navigation.Navigation{}).
		Where("name = ? AND position = ?", name, position)

	if excludeID > 0 {
		query = query.Where("id != ?", excludeID)
	}

	err := query.Count(&count).Error
	if err != nil {
		return false, err
	}

	return count > 0, nil
}

// ExistsByName 检查名称是否存在
func (r *NavigationRepository) ExistsByName(ctx context.Context, name, position string, excludeID uint) (bool, error) {
	return r.CheckNameExists(ctx, name, position, excludeID)
}

// ExistsByURL 检查URL是否存在
func (r *NavigationRepository) ExistsByURL(ctx context.Context, url, position string, excludeID uint) (bool, error) {
	var count int64
	query := r.db.WithContext(ctx).Model(&navigation.Navigation{}).
		Where("url = ? AND position = ?", url, position)

	if excludeID > 0 {
		query = query.Where("id != ?", excludeID)
	}

	err := query.Count(&count).Error
	if err != nil {
		return false, err
	}

	return count > 0, nil
}

// CheckParentExists 检查父导航是否存在
func (r *NavigationRepository) CheckParentExists(ctx context.Context, parentID uint) (bool, error) {
	var count int64
	err := r.db.WithContext(ctx).Model(&navigation.Navigation{}).
		Where("id = ?", parentID).
		Count(&count).Error
	if err != nil {
		return false, err
	}

	return count > 0, nil
}

// GetChildren 获取子导航
func (r *NavigationRepository) GetChildren(ctx context.Context, parentID uint) ([]*navigation.Navigation, error) {
	var navigations []*navigation.Navigation
	err := r.db.WithContext(ctx).
		Where("parent_id = ?", parentID).
		Find(&navigations).Error
	if err != nil {
		return nil, err
	}
	return navigations, nil
}

// GetTree 获取导航树
func (r *NavigationRepository) GetTree(ctx context.Context, position string) ([]*navigation.NavigationNode, error) {
	// 获取给定位置的所有导航
	var navigations []*navigation.Navigation
	err := r.db.WithContext(ctx).
		Where("position = ? AND status = ?", position, navigation.StatusEnabled).
		Order("`order` ASC").
		Find(&navigations).Error
	if err != nil {
		return nil, err
	}

	// 构建导航树
	navMap := make(map[uint]*navigation.NavigationNode)
	var rootNodes []*navigation.NavigationNode

	// 第一遍：构建节点映射
	for _, nav := range navigations {
		node := &navigation.NavigationNode{
			ID:          nav.ID,
			Name:        nav.Name,
			URL:         nav.URL,
			Icon:        nav.Icon,
			Position:    nav.Position,
			Order:       nav.Order,
			Status:      nav.Status,
			Target:      nav.Target,
			Description: nav.Description,
			Children:    []*navigation.NavigationNode{},
			CreatedAt:   utils.TimeToJSONTime(nav.CreatedAt),
			UpdatedAt:   utils.TimeToJSONTime(nav.UpdatedAt),
		}
		navMap[nav.ID] = node
	}

	// 第二遍：构建树结构
	for _, nav := range navigations {
		if nav.ParentID == nil || *nav.ParentID == 0 {
			// 顶级导航
			rootNodes = append(rootNodes, navMap[nav.ID])
		} else {
			// 子导航
			if parent, ok := navMap[*nav.ParentID]; ok {
				parent.Children = append(parent.Children, navMap[nav.ID])
			}
		}
	}

	return rootNodes, nil
}

// BatchUpdateOrder 批量更新导航排序
func (r *NavigationRepository) BatchUpdateOrder(ctx context.Context, items []navigation.OrderItem) error {
	// 使用事务来确保所有更新都成功或都失败
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for _, item := range items {
			err := tx.Model(&navigation.Navigation{}).
				Where("id = ?", item.ID).
				Update("order", item.Order).
				Update("updated_at", time.Now()).Error
			if err != nil {
				return err
			}
		}
		return nil
	})
}

// UpdateStatus 更新导航状态
func (r *NavigationRepository) UpdateStatus(ctx context.Context, id uint, status int) error {
	return r.db.WithContext(ctx).Model(&navigation.Navigation{}).
		Where("id = ?", id).
		Updates(map[string]interface{}{
			"status":     status,
			"updated_at": time.Now(),
		}).Error
}
